'''
Created on Oct 3, 2012

@author: Himanshu
'''
import csv
import numpy as np
import matplotlib.pyplot as plt
import sys
import cPickle as pickle

class Parser():
    '''
    parses the .csv data files. Also contains methods to build training set out of parsed data. 
    For now only collects Acceleration Sensor data. 
    '''

    def __init__(self):
        """
        contructor
        """
        self.timestamps = []
        self.eventTimeStamps = []
        self.x = []
        self.y = []
        self.z = []
        
    def parse(self, fileName, startTime, endTime):
        """
        main parser function. Returns timestamps, event timestamps, x, y, and z acceleration vectors from startTime
        to endTime INCLUSIVE. Stores the parsed data in class features as lists.
        """
        i=0
        rootPath = 'E:\Georgia Tech\CS 6601 - AI\Proj 1\subCheck\subCheck'
        with open(rootPath+"\data\\" + fileName, 'r') as csvfile:
                reader = csv.reader(csvfile, dialect = 'excel')
                for row in reader:
                    if (row[0] != ''):
                        timeStamp = int(row[0])
                        if (timeStamp >= startTime):                
                            if (timeStamp > endTime):                                
                                break
                            eventTimeStamp = int(row[1])
                            xs = float(row[2])
                            ys = float(row[3])
                            zs = float(row[4])
                            self.timestamps.append(timeStamp)
                            self.eventTimeStamps.append(eventTimeStamp)       
                            self.x.append(xs)
                            self.y.append(ys) 
                            self.z.append(zs)                
                    i = i + 1
    
        
    
    def getFeatures(self, mode, startTime, noGestures, gestureInterval, windowSize, smoothSize, sigma, t):
        """
        Builds feature vectors according to mode (desired features) and the startTime. This collects feature vectors
        for multiple instances for one gesture. For example, the 'phone slap' gesture performed noGestures times 
        with gestureInterval seconds between each gesture. All the possible feature vectors extracted will be returned
        """
        
        "Check for empty class features"
        if (self.timestamps.__len__() == 0):
            print "Empty TimeStamps. Run parse script first!"
            return
        
        endTime = startTime + noGestures*gestureInterval
                
        startIndex = None
        "Attempt to find best match starting and ending index. It is made sure none of the gesture signal is lost"
        for i in range(startTime,self.timestamps[0],-1):
            try:
                startIndex = self.timestamps.index(i)
                break
            except ValueError:
                pass
        if (startIndex == None):
            print "Range not in data set"
            return
        
        for i in range(endTime,self.timestamps[-1]):
            try:
                endIndex = self.timestamps.index(i)
                break
            except ValueError:
                pass
        if (endIndex == None):
            print "Range not in data set"
            return
        endIndex += 10
        "Collect feature vectors"
        return self.windowMeanAnalyzer(windowSize, startIndex, endIndex, mode, smoothSize, sigma , t)
        """Notice this does not take in noGestures. It identifies on its own how many gestures it can find in the 
        time interval"""
        

    def windowMeanAnalyzer(self , windowSize, gstartIndex , gendIndex , mode, smoothSize, sigma, t):
        """
        method to automatically identify number of gesture instances and gesture feature vectors based on different modes.
        startIndex and endIndex are derived from getFeatures method. Current supported mode is 'acceleration_norm'.
        """
        " create splices between begin and end time "
        rawFeatures = [self.x[gstartIndex:gendIndex+1] , self.y[gstartIndex:gendIndex+1] , self.z[gstartIndex:gendIndex+1]]
        eventTime = self.eventTimeStamps[gstartIndex:gendIndex+1]
        gestureLength = 5 #approximate
        
        if (mode == 'acceleration_norm'):
            aRaw = np.sum(np.abs(np.transpose(rawFeatures))**2,axis = -1)**(1./2) #net acceleration
            plt.figure()
            plt.plot(eventTime,aRaw)
            plt.hold(True)
            
            " subtract out acceleration mean and calculate window means "
            globalMean = np.mean(aRaw)
            a = np.subtract(aRaw,globalMean)
            a = np.abs(a)
            windowMeans = []
            windowStds = []
            for i in range(0 , gendIndex - gstartIndex + 2 - windowSize):
                localMean = np.mean(a[i:i+windowSize])
                windowMeans.append(localMean)
                localStd = np.std(a[i:i+windowSize])
                windowStds.append(localStd)
            for i in range(0,((windowSize-1)/2)):
                windowMeans.insert(0,0)
                windowMeans.append(0)
            plt.plot(eventTime,windowMeans)
            
            " smooth the window means using a gaussian function "
            if ((smoothSize == 0) or (sigma == 0)):
                smoothWindowMeans = windowMeans
            else:
                smoothWindowMeans = []
                for i in range(0 , gendIndex - gstartIndex + 2 - smoothSize):
                    gaussFilter = self.getFilter(smoothSize,sigma)
                    localWindowMean = np.dot(gaussFilter,a[i:i+smoothSize])
                    smoothWindowMeans.append(localWindowMean)
                for i in range(0,((smoothSize-1)/2)):
                    smoothWindowMeans.insert(0,0)
                    smoothWindowMeans.append(0)
                plt.plot(eventTime,smoothWindowMeans)
            
            " extract interest points based on smoothed window means going over a threshold "
            i = ((windowSize-1)/2)
            interestPoints = []
            interestTimings = []
            #thresh = 0.5*globalStd
            thresh = np.mean(windowStds) + t*np.std(windowStds)
            while( i < (smoothWindowMeans.__len__() - ((windowSize-1)/2) + 1)):
                if (smoothWindowMeans[i] > thresh):
                    " create 5 index cushion before gesture start if possible "
                    if ((i - 5) > 0):
                        startTime = eventTime[i-5] 
                        startIndex = i - 5
                    else: 
                        startTime = eventTime[i]
                        startIndex = i 
                        
                    " guess gesture end time by gestureLength"
                    guessEndTime = startTime + gestureLength*(10**9)
                    while ((i<eventTime.__len__()) and (eventTime[i]<guessEndTime)):
                        i += 1
                    " however, keep recording till smooth mean drops below threshold"
                    while ((i<smoothWindowMeans.__len__()) and (smoothWindowMeans[i] > thresh)):
                        i += 1
                    " if possible, create a 5 index cushion after end of gesture"
                    if ((i +5 ) < smoothWindowMeans.__len__()):
                        i += 5
                    else:
                        count = 0
                        while ((count < 5) and (i < smoothWindowMeans.__len__())):
                            i += 1
                            count += 1
                    endIndex = i
                    if (endIndex == eventTime.__len__()):
                        endIndex -=1
                        
                    "record the start and end of the gesture as an interest point"
                    interestPoints.append((startIndex,endIndex))
                    interestTimings.append((startTime,eventTime[endIndex]))
                i += 1
            print "Index harvested: " , interestPoints
            print "Event Time harvested: " , interestTimings
            for point in interestTimings:
                plt.axvline(x = point[0] , color = 'r')
                plt.axvline(x = point[1] , color = 'g')
            
            "Harvest features"
            features = []
            times = []
            for point in interestPoints:
                features.append(aRaw[point[0]:point[1]])
                times.append(eventTime[point[0]:point[1]])
            print "Total number of feature vectors harvested: " , features.__len__() , "."
            featureTime = (features,times)
            plt.show()
            return featureTime
        else:
            print "Invalid mode. Allowed modes: acceleration_norm'"
            return
    
    def getFilter(self,size,sigma):
        """
        return an array with a gaussian function of size and variance sigma
        """
        mean = (size-1)/2
        gaussFilter = [0]*size
        for i in range(0,size):
            gaussFilter[i] = np.exp(-1.*(i-mean)*(i-mean)/(2*sigma*sigma))
        gaussFilter /= np.trapz(gaussFilter)
        return gaussFilter
        
    def buildPickles(self, globalStart):
        """The feature collector is highly customizable. The def is as follows:
        def getFeatures(mode, startTime, noGestures, gestureInterval, windowSize, smoothSize, sigma, t)
        windowSize is for computing means
        smoothSize, sigma are for smoothing means (value of 0 means no smoothing)
        t is related to threshold on smoothed means for qualifying as gesture"""  
        
        mode = 'acceleration_norm'
        
#        windowSize = 5;
#        smoothSize = 25;
#        sigma = 3;
#        t = 1;
#        #def gestureTuner(self, mode, startTime, windowSize, smoothSize, sigma, t, gestureName, pickleName):
#        self.gestureTuner(mode, globalStart, windowSize, smoothSize, sigma, t, 'Triangle')
#        
#        windowSize = 31;
#        smoothSize = 0;
#        sigma = 0;
#        t = 0.2;  
#        #def gestureTuner(self, mode, startTime, windowSize, smoothSize, sigma, t, gestureName, pickleName):
#        self.gestureTuner(mode, globalStart + 180, windowSize, smoothSize, sigma, t, 'Window_Open')
#        
#        windowSize = 5;
#        smoothSize = 25;
#        sigma = 5;
#        t = 1.3;
#        #def gestureTuner(self, mode, startTime, windowSize, smoothSize, sigma, t, gestureName, pickleName):
#        self.gestureTuner(mode, globalStart + 360, windowSize, smoothSize, sigma, t, 'Window_Close')
#        
#        windowSize = 31;
#        smoothSize = 0;
#        sigma = 0;
#        t = 0.35;
#        #def gestureTuner(self, mode, startTime, windowSize, smoothSize, sigma, t, gestureName, pickleName):
#        self.gestureTuner(mode, globalStart + 600, windowSize, smoothSize, sigma, t, 'Fire_On')
#        
#        windowSize = 21;
#        smoothSize = 0;
#        sigma = 0;
#        t = 0.3;
#        #def gestureTuner(self, mode, startTime, windowSize, smoothSize, sigma, t, gestureName, pickleName):
#        self.gestureTuner(mode, globalStart + 780, windowSize, smoothSize, sigma, t, 'Fire_Off')
#        
#        windowSize = 31;
#        smoothSize = 0;
#        sigma = 0;
#        t = 0.35;
#        #def gestureTuner(self, mode, startTime, windowSize, smoothSize, sigma, t, gestureName, pickleName):
#        self.gestureTuner(mode, globalStart + 960, windowSize, smoothSize, sigma, t, 'Throw_Money')
#        
#        windowSize = 31;
#        smoothSize = 0;
#        sigma = 0;
#        t = 0.35;
#        #def gestureTuner(self, mode, startTime, windowSize, smoothSize, sigma, t, gestureName, pickleName):
#        self.gestureTuner(mode, globalStart + 1260, windowSize, smoothSize, sigma, t, 'Swing_Phone')
#        
#        windowSize = 21;
#        smoothSize = 0;
#        sigma = 0;
#        t = 0.3;
#        #def gestureTuner(self, mode, startTime, windowSize, smoothSize, sigma, t, gestureName, pickleName):
#        self.gestureTuner(mode, globalStart + 1440, windowSize, smoothSize, sigma, t, 'W')
#        
#        windowSize = 19;
#        smoothSize = 11;
#        sigma = 1;
#        t = 1;
#        #def gestureTuner(self, mode, startTime, windowSize, smoothSize, sigma, t, gestureName, pickleName):
#        self.gestureTuner(mode, globalStart + 1620, windowSize, smoothSize, sigma, t, 'Tap_phone')

        windowSize = 31;
        smoothSize = 0;
        sigma = 0;
        t = 0.35;
        #def gestureTuner(self, mode, startTime, windowSize, smoothSize, sigma, t, gestureName, pickleName):
        #self.gestureTuner(mode, globalStart, windowSize, smoothSize, sigma, t, 'Multi_Finger_Snap')
        
        windowSize = 31;
        smoothSize = 0;
        sigma = 0;
        t = 0.35;
        #def gestureTuner(self, mode, startTime, windowSize, smoothSize, sigma, t, gestureName, pickleName):
        #self.gestureTuner(mode, globalStart + 180, windowSize, smoothSize, sigma, t, 'Flick_Air')
        
        windowSize = 31;
        smoothSize = 0;
        sigma = 0;
        t = 0.35;
        #def gestureTuner(self, mode, startTime, windowSize, smoothSize, sigma, t, gestureName, pickleName):
        self.gestureTuner(mode, globalStart + 720, windowSize, smoothSize, sigma, t, 'Door_Open')
        
        windowSize = 31;
        smoothSize = 0;
        sigma = 0;
        t = 0.35;
        #def gestureTuner(self, mode, startTime, windowSize, smoothSize, sigma, t, gestureName, pickleName):
        self.gestureTuner(mode, globalStart + 900, windowSize, smoothSize, sigma, t, 'Door_Close')
        

            
    def gestureTuner(self, mode, startTime, windowSize, smoothSize, sigma, t, gestureName):
        while(True):
            #def getFeatures(mode, startTime, noGestures, gestureInterval, windowSize, smoothSize, sigma, t)
            features = self.getFeatures(mode, startTime, 10, 15, windowSize, smoothSize, sigma , t)
            response = raw_input('Please enter y if satisfied an n if not\n')
            if (response == 'y'):
                pickleName = gestureName + '.pkl'
                self.writePickles(features, gestureName, pickleName)
                print 'Pickle written, name = ', pickleName
                break
            else:
                print "Last entered values: Window Size = ", windowSize
                print "smoothing size: ", smoothSize, "\nSigma = ", sigma, "\nThreshold =  ", t
                windowSize = int(raw_input('Please enter averaging window Size: \n'))
                smoothSize = int(raw_input('Please enter smoothing window Size: \n'))
                sigma = float(raw_input('Please enter smoothing sigma: \n'))
                t = float(raw_input('Please enter threshold: \n'))
        
                
    def writePickles(self, features, featureName, pickleName):
        noFeatureVectors = features[0].__len__()
        print "If this number is over 10, the program will exit unsuccesfully!"
        if (noFeatureVectors>10):
            sys.exit(1)
        "Dump to pickle file"
        output = open(pickleName, 'wb')
        pickle.dump(featureName,output)
        pickle.dump(noFeatureVectors,output)
        pickle.dump(features,output)
                
            
        
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
                